/*
 * Copyright (c) 2025 Li Auto Inc. and its affiliates
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <utility>
#include <utility>
#include <cstring>
#include <algorithm>

#include <elog/Log.hpp>
#include <ertps/utils/LocatorAlias.h>
#include <deps/common/IPLocator.h>
#include <deps/common/IPFinder.h>
#include <transport/TransportFactory.hpp>
#include <transport/TransportInterface.h>
#include <transport/ReceiverResource.h>
#include <transport/SenderResource.h>
#include "RAWTransport.h"

using namespace std;

namespace vbs {
namespace transport {

using IPLocator = vbsutil::xmlparser::IPLocator;
using IPFinder = vbsutil::xmlparser::IPFinder;
using PortParameters = vbsutil::xmlparser::PortParameters;

using SenderResource = vbs::transport::SenderResource;

RAWTransportDescriptor::RAWTransportDescriptor()
    : SocketTransportDescriptor(s_maximumMessageSize, s_maximumInitialPeersRange), m_output_raw_socket(0) {}

bool RAWTransportDescriptor::operator==(const RAWTransportDescriptor& t) const {
    return ((this->m_output_raw_socket == t.m_output_raw_socket) && (this->non_blocking_send == t.non_blocking_send) &&
            (SocketTransportDescriptor::operator==(t)));
}

TransportInterface* RAWTransportDescriptor::create_transport() const {
    try {
        return new RAWTransport(*this);
    } catch (const std::exception& e) {
        logError(TRANSPORT, "new RAWTransport(*this) catch error: " << e.what());
        return nullptr;
    }
}

TransmitorType RAWTransportDescriptor::get_type() const {
    return TransmitorType::TRANSMITOR_TYPE_RAW;
}

RAWTransport::RAWTransport(const RAWTransportDescriptor& descriptor)
    : TransportInterface(LOCATOR_KIND_RAW), configuration_(descriptor) {}

RAWTransport::~RAWTransport() {}

bool RAWTransport::init() {
    logDebug(RTPS_MSG_OUT, "init RAWTransport.");
    return true;
}

bool RAWTransport::send(octet* send_buffer, uint32_t send_buffer_size,
                        vbsutil::xmlparser::LocatorsIterator* /*destination_locators_begin*/,
                        vbsutil::xmlparser::LocatorsIterator* /*destination_locators_end*/) {
    (void)send_buffer;
    (void)send_buffer_size;
    logError(RTPS_MSG_OUT, "RAW transport Send faild:  RAW channel is not support.");
    return false;
}

bool RAWTransport::OpenInputChannel(const Locator& locator, std::shared_ptr<ReceiverResource> receiver,
                                    uint32_t maxMsgSize) {
    elogInfo(RTPS_MSG_OUT, "create  RAW transport channel resource.. maxMsgSize=" << maxMsgSize);

    std::string locatorAddressStr = IPLocator::toIPv4string(locator);
    channel_resource_ = new RAWChannelResource(maxMsgSize, locator, locatorAddressStr, std::move(receiver));

    return (channel_resource_ != nullptr);
}

//! Removes the listening socket for the specified port.
bool RAWTransport::CloseInputChannel(const Locator&) {
    logDebug(RTPS_MSG_OUT, "CloseInputChannel");
    if (channel_resource_ != nullptr) {
        channel_resource_->disable();
        try {
            channel_resource_->release();
        } catch (const std::exception& e) {
            elogWarning(RTPS_MSG_OUT, "channel->release() catch :" << e.what());
        }
        channel_resource_->clear();
        delete channel_resource_;

        channel_resource_ = nullptr;
    }

    return true;
}

//! Checks whether there are open and bound sockets for the given port.
bool RAWTransport::IsInputChannelOpen(const Locator& /*locator*/) const {
    return (channel_resource_ != nullptr);
}

//! Reports whether Locators correspond to the same port.
bool RAWTransport::DoInputLocatorsMatch(const Locator& /*locator*/, const Locator& /*locator*/) const {
    return true;
}

//! Checks for TCP kinds.
bool RAWTransport::IsLocatorSupported(const Locator& locator) const {
    return (locator.kind == LOCATOR_KIND_RAW);
}

//! Opens a socket on the given address and port (as long as they are white listed).
bool RAWTransport::OpenOutputChannel(SendResourceList& sender_resource_list, const Locator& locator) {
    (void)sender_resource_list;
    (void)locator;
    // logDebug(RTPS_MSG_OUT, "RAWTransport open output channel locator:"<<locator);

    // if (!IsLocatorSupported(locator)) {
    //     return false;
    // }

    // if (!bSendResourceExist_) {
    //     sender_resource_list.emplace_back(static_cast<SenderResource*>(new RAWSenderResource(*this)));
    //     bSendResourceExist_ = true;
    // }
    return true;
}

Locator RAWTransport::RemoteToMainLocal(const Locator& locator) const {
    return locator;
}

bool RAWTransport::transform_remote_locator(const Locator& remote_locator, Locator& result_locator) const {
    if (IsLocatorSupported(remote_locator)) {
        result_locator = remote_locator;
    }
    return true;
}

LocatorList RAWTransport::NormalizeLocator(const Locator& locator) {
    LocatorList list;
    if (IsLocatorSupported(locator)) {
        list.push_back(locator);
    }
    return list;
}

bool RAWTransport::is_local_locator(const Locator& /*locator*/) const {
    return false;
}

void RAWTransport::AddDefaultOutputLocator(LocatorList& /*locatorlist*/) {}

bool RAWTransport::getDefaultMetatrafficMulticastLocators(LocatorList& /*locators*/,
                                                          uint32_t /*metatraffic_multicast_port*/) const {
    return true;
}

bool RAWTransport::getDefaultMetatrafficUnicastLocators(LocatorList& /*locators*/,
                                                        uint32_t /*metatraffic_unicast_port*/) const {
    return true;
}

bool RAWTransport::getDefaultUnicastLocators(LocatorList& /*locators*/, uint32_t /*unicast_port*/) const {
    return true;
}

bool RAWTransport::fillMetatrafficMulticastLocator(Locator& /*locator*/,
                                                   uint32_t /*metatraffic_multicast_port*/) const {
    return true;
}

bool RAWTransport::fillMetatrafficUnicastLocator(Locator& /*locator*/, uint32_t /*metatraffic_unicast_port*/) const {
    return true;
}

bool RAWTransport::configureInitialPeerLocator(Locator& /*locator*/,
                                               const vbsutil::xmlparser::PortParameters& /*port_params*/,
                                               uint32_t /*domainId*/, LocatorList& /*list*/) const {
    return true;
}

bool RAWTransport::fillUnicastLocator(Locator& /*locator*/, uint32_t /*well_known_port*/) const {
    return true;
}

bool RAWTransport::is_locator_allowed(const Locator& locator) const {
    return (locator.kind == LOCATOR_KIND_RAW);
}

}  // namespace transport
}  // namespace vbs
